﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpGroupEventHandler.h"
#include "IGroupEventArgs.h"
#include "IElementJoin.h"
#include "IDocument.h"
#include "IElementCut.h"
#include "IElementOpening.h"
#include "NdbDataInterface.h"
#include "IGroup.h"
#include "IGroupType.h"
#include "IGeometryRelationshipComponent.h"
#include "GroupRelationshipUtils.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace gcmp
{
    // Element被拷贝后放入GroupType时，需要将组外关系删除
    void AdjustElementRelationship(IDocument* pDoc, const IElement* pSrcElement,
        ElementId targetElementId, ElementId groupId, bool isSyncFromTypeToGroup)
    {
        if (!pDoc || !pSrcElement)
            return;
        IElement* pTargetElement = pDoc->GetElement(targetElementId);
        IGroup* pGroup = quick_cast<IGroup>(pDoc->GetElement(groupId));
        if (!pTargetElement || !pGroup)
            return;

        const IGeometryRelationshipComponent* pSrcGeometryRelationshipComponent = pSrcElement->GetGeometryRelationshipComponent();
        IGeometryRelationshipComponent* pTargetGeometryRelationshipComponent = pTargetElement->GetGeometryRelationshipComponent();
        if (!pSrcGeometryRelationshipComponent || !pTargetGeometryRelationshipComponent)
            return;

        bool result = true;
        // 调整连接关系
        if (pSrcGeometryRelationshipComponent->GetJoinRelationship() && pTargetGeometryRelationshipComponent->GetJoinRelationship())
        {
            result = GroupRelationshipUtils::AdjustJoinRelationship(pSrcGeometryRelationshipComponent->GetJoinRelationship(),
                pTargetGeometryRelationshipComponent->GetJoinRelationship(), pGroup, isSyncFromTypeToGroup);
            DBG_WARN_AND_RETURN_VOID_UNLESS(result, L"调整连接关系失败",L"GDMPLab",L"2024-03-30");
        }  

        // 调整扣减关系
        if (pSrcGeometryRelationshipComponent->GetCutRelationship() && pTargetGeometryRelationshipComponent->GetCutRelationship())
        {
            result = GroupRelationshipUtils::AdjustCutRelationship(pSrcGeometryRelationshipComponent->GetCutRelationship(),
                pTargetGeometryRelationshipComponent->GetCutRelationship(), pGroup, isSyncFromTypeToGroup);
            DBG_WARN_AND_RETURN_VOID_UNLESS(result, L"调整扣减关系失败",L"GDMPLab",L"2024-03-30");
        }

        // 调整大东关系
        if (pSrcGeometryRelationshipComponent->GetOpeningRelationship() && pTargetGeometryRelationshipComponent->GetOpeningRelationship())
        {
            result = GroupRelationshipUtils::AdjustOpeningRelationship(pDoc, pSrcElement, pTargetElement, pGroup, isSyncFromTypeToGroup);
            DBG_WARN_AND_RETURN_VOID_UNLESS(result, L"调整打洞关系失败",L"GDMPLab",L"2024-03-30");
        }
    }
}

void UpdateRelationships(IDocument* pDoc, const std::vector<ElementId>& srcElementIds,
    const std::vector<ElementId>& targetElementIds, ElementId groupId, ElementId groupTypeId, bool isSyncFromTypeToGroup, GroupSyncStatus groupStatus)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    ////////////////////////////////////////////////////
    // 暂时代码
    // 后面扣减、打洞、连接统一处理
    ElementId srcGroupId = groupId;
    ElementId targetGroupId = groupTypeId;
    if (isSyncFromTypeToGroup)
    {
        srcGroupId = groupTypeId;
        targetGroupId = groupId;
    }
    ////////////////////////////////////////////////////

    for (int i = 0; i < srcElementIds.size(); ++i)
    {
        std::unordered_map<ElementId, ElementId, ElementIdHash> oldToNewMap;
        const IElement* pSrcElement = pDoc->GetElement(srcElementIds[i]);
        if (const IElementJoin* pSrcElementJoin = quick_cast<const IElementJoin>(pSrcElement))
        {
            if (groupStatus == GroupSyncStatus::InChangeStatus)
            {
                // 连接关系添加到组后，需要重新计算连接关系并替换连接关系记录
                oldToNewMap = GroupRelationshipUtils::ComputeElementJoinRelationships(pDoc, pSrcElementJoin, targetElementIds[i], srcGroupId,
                    targetGroupId);
            }
        }
        else if (const IElementCut* pSrcElementCut = quick_cast<const IElementCut>(pSrcElement))
        {
            if (groupStatus == GroupSyncStatus::InChangeStatus)
            {
                oldToNewMap = GroupRelationshipUtils::ComputeElementCutRelationships(pDoc, pSrcElementCut, targetElementIds[i], srcGroupId,
                    targetGroupId);
            }

        }
        else if (const IElementOpening* pSrcElementOpening = quick_cast<const IElementOpening>(pSrcElement))
        {
            if (groupStatus == GroupSyncStatus::InChangeStatus)
            {
                oldToNewMap = GroupRelationshipUtils::ComputeElementOpeningRelationships(pDoc, pSrcElementOpening, targetElementIds[i], srcGroupId,
                    targetGroupId);
            }            
        }
        else if (const IGroup* pSrcGroup = quick_cast<const IGroup>(pSrcElement))
        {
            const IGroup* pTargetGroup = quick_cast<const IGroup>(pDoc->GetElement(targetElementIds[i]));
            if (pTargetGroup)
            {
                const std::map<ElementId, ElementId>& groupToTypeIdMap = pSrcGroup->GetGroupToTypeIdMap();
                const std::map<ElementId, ElementId>& typetoGroupIdMap = pTargetGroup->GetTypeToGroupIdMap();

                std::vector<ElementId> subSrcElements, subTargetElementIds;
                FOR_EACH(iter, groupToTypeIdMap)
                {
                    auto typeItem = typetoGroupIdMap.find(iter.second);
                    if(typeItem == typetoGroupIdMap.cend())
                        continue;

                    subSrcElements.emplace_back(iter.first);
                    subTargetElementIds.emplace_back(typeItem->second);
                }
                UpdateRelationships(pDoc, subSrcElements, subTargetElementIds, groupId, groupTypeId, isSyncFromTypeToGroup, groupStatus);
            }
        }
        else
        {
            if (groupStatus == GroupSyncStatus::InAddStatus || 
                groupStatus == GroupSyncStatus::InChangeStatus || 
                groupStatus == GroupSyncStatus::InCreateStatus)
                AdjustElementRelationship(pDoc, pSrcElement, targetElementIds[i], groupId, isSyncFromTypeToGroup);
            if (groupStatus == GroupSyncStatus::InChangeStatus)
            {
                oldToNewMap = GroupRelationshipUtils::ComputeElementRelationships(pDoc, pSrcElement, targetElementIds[i], srcGroupId,
                    targetGroupId);
            }
        }

        FOR_EACH(item, oldToNewMap)
        {
            IElement* pElement = pDoc->GetElement(item.second);
            if (pElement)
                NdbDataInterface::ReplaceElementIds(quick_cast<NdbObjectSchematic>(pElement), oldToNewMap);
        }
    }
}

GbmpGroupEventHandler::GbmpGroupEventHandler()
    : m_groupStatus(GroupSyncStatus::InNoneStatus)
    , m_groupId(ElementId::InvalidID)
    , m_groupTypeId(ElementId::InvalidID)
{
}

void GbmpGroupEventHandler::On(IGroupEventArgs* pArgs)
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pArgs, L"pArgs是空指针",L"GDMPLab",L"2024-03-30");
    IDocument* pDoc = pArgs->GetDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc是空指针",L"GDMPLab",L"2024-03-30");

    switch (pArgs->GetType())
    {
    case GroupEventType::PreCreate:
        {
            m_groupStatus = GroupSyncStatus::InCreateStatus;
        }
        break;
    case GroupEventType::PostCreate:
        {
            // 创建组类型或组实例时会触发该事件
            // 此处依赖拷贝策略就可以达到同步的效果
           // 如果产品发现有一些与拷贝策略不同的更新逻辑需要处理，可在该事件里面实现
            m_groupStatus = GroupSyncStatus::InNoneStatus;
        }
        break;
    case GroupEventType::PreSyncAdd:
        m_groupStatus = GroupSyncStatus::InAddStatus;
        break;
    case GroupEventType::PostSyncAdd:
        UpdateRelationships(pDoc, {pArgs->GetSourceElementId()}, {pArgs->GetTargetElementId()}, m_groupId, m_groupTypeId, pArgs->IsGroupTypeToGroupSync(), m_groupStatus);
        m_groupStatus = GroupSyncStatus::InNoneStatus;
        break;
    case GroupEventType::PreSyncChange:
        m_groupStatus = GroupSyncStatus::InChangeStatus;
        break;
    case GroupEventType::PostSyncChange:
        UpdateRelationships(pDoc, {pArgs->GetSourceElementId()}, {pArgs->GetTargetElementId()}, m_groupId, m_groupTypeId, pArgs->IsGroupTypeToGroupSync(), m_groupStatus);
        m_groupStatus = GroupSyncStatus::InNoneStatus;
        break;
    case GroupEventType::PreGroupTypeUpdate:
    case GroupEventType::PostGroupTypeUpdate:
        m_groupId = pArgs->GetSourceElementId();
        m_groupTypeId = pArgs->GetTargetElementId();
        break;
    case GroupEventType::PreGroupUpdate:
    case GroupEventType::PostGroupUpdate:
        m_groupTypeId = pArgs->GetSourceElementId();
        m_groupId = pArgs->GetTargetElementId();
        break;
    default:
        break;
    }
}

GbmpGroupEventHandler* GbmpGroupEventHandler::Get()
{
    static OwnerPtr<GbmpGroupEventHandler> g_GbmpGroupEventHandler = NEW_AS_OWNER_PTR(GbmpGroupEventHandler);
    return g_GbmpGroupEventHandler.get();
}
